import logging
import subprocess

from pydantic import BaseModel
from rich.console import Console

logger = logging.getLogger(__name__)


class RunCommandResult(BaseModel):
    return_code: int = 0
    stdout: str = None
    stderr: str = None

    def success(self) -> bool:
        return self.return_code == 0


class RpmPackageInfo(BaseModel):
    name: str
    version: str
    arch: str
    location: str

    @classmethod
    def from_file(cls, path: str) -> "RpmPackageInfo":
        """
        Extracts RPM package metadata from a file at `path`.

        Args:
            path: Path to the RPM file.

        Returns:
            RpmPackageInfo: A Pydantic model containing the parsed RPM metadata.

        Raises:
            ValueError: If the RPM file is invalid or metadata cannot be parsed.
        """
        try:
            result = subprocess.run(
                ["rpm", "-qpi", path],
                capture_output=True,
                text=True,
                check=True
            )
            output = result.stdout
        except subprocess.CalledProcessError as e:
            raise ValueError(f"Failed to parse RPM metadata: {e.stderr}") from e

        # Extract name, version, and architecture from the rpm output
        info = {}
        for line in output.splitlines():
            if line.startswith("Name"):
                info["name"] = line.split(": ")[1].strip()
            elif line.startswith("Version"):
                info["version"] = line.split(": ")[1].strip()
            elif line.startswith("Architecture"):
                info["arch"] = line.split(": ")[1].strip()

        # Handle missing required fields
        if "name" not in info:
            raise ValueError("Could not find package name")
        if "version" not in info:
            raise ValueError("Could not find package version")
        if "arch" not in info:
            raise ValueError("Could not find package architecture")

        info["location"] = path
        return cls(**info)


class RpmPackageManager:

    def __init__(self, console: Console = None):
        self.console = console if console else Console()

    def install(self, package: RpmPackageInfo) -> RunCommandResult:
        """Installs an RPM package using dnf with real-time output."""
        is_installed = self.is_installed(package)
        if is_installed:
            self.console.print(f"Package {package.name} is already installed")
            return RunCommandResult()
        command = ["sudo", "dnf", "install", package.location, "-y"]
        result = self._run_command(command, True)
        return result

    def uninstall(self, package: RpmPackageInfo) -> RunCommandResult:
        """Uninstalls an RPM package using dnf with real-time output."""
        is_installed = self.is_installed(package)
        if is_installed:
            command = ["sudo", "dnf", "remove", package.name, "-y"]
            return self._run_command(command, True)
        else:
            self.console.print(f"No package {package.name} to uninstall")

    def reinstall(self, package: RpmPackageInfo) -> RunCommandResult:
        """Reinstall an RPM package using dnf with real-time output."""
        command = ["sudo", "dnf", "reinstall", package.location, "-y"]
        return self._run_command(command, True)

    def is_installed(self, package: RpmPackageInfo) -> bool:
        """Checks whether an RPM package is installed using rpm -q."""
        command = ["rpm", "-q", package.name]
        response = self._run_command(command, False)
        return "is not installed" not in response.stdout

    def _run_command(self, command: list[str], stream_output: bool = False) -> RunCommandResult:
        """Runs a command with real-time output and error handling."""
        try:
            process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                bufsize=1
            )

            stdout = []
            stderr = []
            if stream_output:
                while True:
                    output = process.stdout.readline()
                    if not output:
                        break
                    self.console.print(output.strip())
                    stdout.append(output)

                for line in process.stderr:
                    self.console.print(line.strip())
                    stderr.append(line)

            process.wait()
            return RunCommandResult(
                return_code=process.returncode,
                stdout="".join(stdout) if stdout else process.stdout.read(),
                stderr="".join(stderr) if stderr else process.stderr.read()
            )
        except Exception as e:
            raise RuntimeError(f"Failed to run command {command}") from e


if __name__ == "__main__":
    mgr = RpmPackageManager()
    print(mgr.is_installed("mcp-servers-system_monitor_mcp"))
    mgr.uninstall("mcp-servers-system_monitor_mcp")
    mgr.install("mcp-servers-system_monitor_mcp")
